/**
 * @author Giuseppe Mendola
 * @author Fatemeh Chegini
 * @author Nicolas Latorre
 */

package sa4

import javax.servlet.http._
import scala.xml._
import javax.servlet.ServletConfig
import sa4.form._
import sa4.session._
import sa4.template._
import sa4.routing._
import sa4.persistence._
import org.squeryl.KeyedEntity
import org.squeryl.SessionFactory
import org.squeryl.Session
import org.squeryl.adapters.H2Adapter
import scala.util.matching.Regex
import org.squeryl.adapters.H2Adapter
import java.sql.Timestamp
import java.sql.Date
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.dsl.ast.LogicalBoolean
import org.squeryl.annotations.OptionType
//IMPORTANT to let this app work you have to replace in <servlet-class>sa4.FrameworkServlet</servlet-class> with <servlet-class>sa4.MyShop</servlet-class>

// This is a very simple Scala servlet.
// If you rename this class, you must also change the file
// src/main/webapp/WEB-INF/web.xml to use the new name.
class MyShop extends HttpServlet {
  type HandlerFunction = (HttpServletRequest, HttpServletResponse) => Unit
  val routing = new Routing()
  val template = new Template()
  val session = new SessionManagement()
  val persistence = new Persistence()

  /**
   * Function that initializes the servlet
   * @param config Configuration for the servlet
   */
  override def init(config: ServletConfig) {
    super.init(config)
    //Starting the database
    Class.forName("org.h2.Driver")

    SessionFactory.concreteFactory = Some(() =>
      Session.create(
        java.sql.DriverManager.getConnection("jdbc:h2:" + System.getProperty("user.dir") + "/test"),
        new H2Adapter))

    transaction {
      Shop.drop
    }

    transaction {
      Shop.create
    }

    transaction {

      Shop.clients.insert(new Client("Mimmo", "bob@cat.ch", "boobie"))
      Shop.clients.insert(new Client("Giuseppe", "mendola5@hotmail.com", "1234"))
      val consolePlay = Shop.products.insert(new Product("Playstation 3", 250))
      val consoleWii = Shop.products.insert(new Product("Wii", 150))
      val consoleXbox = Shop.products.insert(new Product("Xbox", 200))
      val controllerPlay = Shop.products.insert(new Product("Controller Playstation 3", 69))
      val controllerWii = Shop.products.insert(new Product("Controller Wii", 100))
      val controllerXbox = Shop.products.insert(new Product("Controller Xbox", 49))
    }

    //Adding some route
    routing.addRoute("/start", loadHome)
    routing.addRoute("/index", productTemp)
    routing.addRoute("/loginPage", clientLog)
    routing.addRoute("/listclient", listClients)
    routing.addRoute("/register", register)
    routing.addRoute("/add", cart)
    routing.addRoute("/MyShop.css", css)
    //    
  }

  /**
   * Function that manages requests and response on the server
   * @param req Request from the servlet
   * @param resp Response to the servlet
   */
  override def service(req: HttpServletRequest, resp: HttpServletResponse) {
    //Session setup
    var currentContext = session.getCurrentContext
    currentContext = session.newContext(req, resp, currentContext)
    currentContext().session.setMaxInactiveInterval(60)

    //Routing dispatch
    if (routing.tableDispatch(req, resp))
      return
    resp.sendError(HttpServletResponse.SC_NOT_FOUND)

  }

  def loadHome(req: HttpServletRequest, resp: HttpServletResponse) = {
    val xml = template.loadPage("index2.html")
    val html = prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  def css(req: HttpServletRequest, resp: HttpServletResponse) {
    val css = template.loadCss("MyShop.css")
    resp.getWriter.write(css)
    resp.setContentType("text/css")
  }

  def cart(req: HttpServletRequest, resp: HttpServletResponse) {
    val xml = buyed(req.getParameter("q").toLong)
    val html = template.processTemplate(xml)
    //println(html)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  //client Login template
  def clientLog(req: HttpServletRequest, resp: HttpServletResponse) {
    val xml = template.setUpTemplate("templateLoginShop.xml")
    val html = prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }
  //write list of clients Page
  def listClients(req: HttpServletRequest, resp: HttpServletResponse) {
    val xml = clientsPage
    val html = prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }
  //write wrongPassword template
  def wrongPassword(req: HttpServletRequest, resp: HttpServletResponse) {
    val xml = wrongPass
    val html = template.processTemplate(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }
  //write register Page
  def register(req: HttpServletRequest, resp: HttpServletResponse) {
    val xml = template.setUpTemplate("templateRegistrationShop.xml")
    val html = prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }
  //write productList template
  def productTemp(req: HttpServletRequest, resp: HttpServletResponse) {
    val xml = productList
    val html = template.processTemplate(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }

  def login(name: String, pass: String) = {
    var toReturn: Option[Long] = None
    transaction {
      var res = from(Shop.clients)(c =>
        where(c.name === name and c.pass === pass)
          select (c.id))

      if (res != null)
        toReturn = res.headOption
    }

    toReturn match {
      case Some(id: Long) =>
        println(id)
        correctPass
      case _ =>
        wrongPass

    }
  }

  def addProduct() {
    println()
  }

  //FORM

  /**
   * Prepare the form
   * @param template The template of the form we are using
   * @return NodeSeq The template changed
   */
  def prepareForm(template: NodeSeq): NodeSeq = {
    import scala.xml.transform._
    import scala.xml.Null // hide scala.Null
    object MakeForm extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case form: Elem if form.label == "form" =>
          val Some(handler) = form.attribute("handler")
          var Some(method) = form.attribute("method")

          // Make the handler and install in routeTable
          val (uri, f: HandlerFunction) = makeHandler(handler.text)

          routing.addRoute(uri, f)

          // Add an action to the form.
          form % Attribute(None, "action", Text(uri), Null)
        case n => n
      }
    }
    template.map(ns => new RuleTransformer(MakeForm)(ns))
  }

  /**
   * Creates a new path with its corresponding function
   * @param s
   * @return String A new path
   * @return HandlerFunction The corresponding function
   */
  def makeHandler(s: String): (String, HandlerFunction) = {
    // Parse the handler string.
    val q = "^(\\w+)\\((.*)\\)$".r

    val (methodName, argNames) = s match {
      case q(methodName, argNames) => (methodName, argNames.split(",").map(_.trim))
      case _ => throw new RuntimeException("bad handler: " + s)
    }

    // Look for a method that takes the right number of strings.
    val method = this.getClass.getMethod(methodName, argNames.map(_ => classOf[String]): _*)
    val h = (req: HttpServletRequest, resp: HttpServletResponse) => {
      val result = method.invoke(this, argNames.map(k => req.getParameter(k)): _*)
      result match {
        case s: String =>
          resp.getWriter.write(s)
        case html: NodeSeq =>
          resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
      }
    }
    (handlerURI(methodName), h)
  }

  /**
   * Constructs a new path with the method name
   * @param methodName The method that will be used as path
   */
  def handlerURI(methodName: String) = {
    "/" + methodName
  }

  /*
   * *************persistence*******************
   */
  def clientsPage = {
    <html>
      <head>
        <title>Clients:</title>
      </head>
      <body>
        <h3>All clients</h3>
        <dl>
          {
            transaction {
              for (a <- Shop.clients)
                yield <dt>{ a.name + "  " + a.email + " " + a.pass }</dt>
            }
          }
        </dl>
      </body>
    </html>
  }
  def wrongPass = {
    <html>
      <head>
        <title>
          Wrong Pass
        </title>
      </head>
      <body>
        <font color="red">Wrong Pass</font>
        <p>the password or the username was wrong</p>
        <form method="post" action="/loginPage">
          <p>
            <input type="submit" value="Retry"/>
          </p>
        </form>
      </body>
    </html>
  }

  def correctPass = {
    <html>
      <head>
        <title>
          Wrong Pass
        </title>
      </head>
      <body>
        <font color="green">Correct Pass</font>
        <p>Your are now logged in our amazing shop, please press continue to enter</p>
        <form method="post" action="/index">
          <p>
            <input type="submit" value="Continue"/>
          </p>
        </form>
      </body>
    </html>
  }

  def buyed(product: Long) = {
    transaction {
      Shop.carts.insert(new Cart(0, product))
    }

    <surround with="index2.html" at="cart">
      {
        transaction {
          for (
            a <- from(Shop.carts)(a => where(a.client === 0) select (a));
            b <- Shop.products.lookup(a.product)
          ) yield <li>{ b.name }</li>
        }
      }
    </surround>
    <surround with="index2.html" at="here">
      {
        transaction {

          for (a <- Shop.products)
            yield <dt><a href={ "/add?q=" + a.id }>{ a.name } </a>price: { a.cost }$</dt>
        }
      }
    </surround>
  }

  def productList = {
    <surround with="index2.html" at="here">
      {
        transaction {

          for (a <- Shop.products)
            yield <dt><a href={ "/add?q=" + a.id }>{ a.name } </a>price: { a.cost }$</dt>
        }
      }
    </surround>
  }

  def postData(name: String, mail: String, pass: String) =
    <html>
      <head>
        <title> Registration Complete</title>
      </head>
      <body>
        {
          transaction {
            Shop.clients.insert(new Client(name, mail, pass))
          }
        }
        <h3>{ name } your registration is complete</h3>
      </body>
    </html>

}
